/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.handlers.commands;

import com.hercules.android.receiver.AndroidReceiver;
import com.hercules.android.receiver.LogUtil;
import com.hercules.android.receiver.handlers.commands.CommandHandlersUtil.ValidationException;
import com.hercules.android.receiver.handlers.events.EventListenerBinder;
import com.hercules.android.receiver.model.commands.XRECommand;
import com.hercules.android.receiver.model.commands.XRESetCommand;
import com.hercules.android.receiver.model.objects.XREAnimationResource;
import com.hercules.android.receiver.model.objects.XREApplication;
import com.hercules.android.receiver.model.objects.XREFlash;
import com.hercules.android.receiver.model.objects.XREFont;
import com.hercules.android.receiver.model.objects.XREHTMLText;
import com.hercules.android.receiver.model.objects.XREHTMLView;
import com.hercules.android.receiver.model.objects.XREImage;
import com.hercules.android.receiver.model.objects.XREObject;
import com.hercules.android.receiver.model.objects.XRERectangle;
import com.hercules.android.receiver.model.objects.XREResource;
import com.hercules.android.receiver.model.objects.XREResourceOptions;
import com.hercules.android.receiver.model.objects.XRESound;
import com.hercules.android.receiver.model.objects.DoNothingSound;
import com.hercules.android.receiver.model.objects.XREStreamResource;
import com.hercules.android.receiver.model.objects.XREStylesheet;
import com.hercules.android.receiver.model.objects.XREServiceProxy;
import com.hercules.android.receiver.model.objects.XREText;
import com.hercules.android.receiver.model.objects.XRETextInput;
import com.hercules.android.receiver.model.objects.XREVideo;
import com.hercules.android.receiver.model.objects.XREView;
import com.hercules.android.receiver.painting.PaintersUtil;
import com.hercules.android.receiver.painting.XREObjectPainterException;
import com.hercules.android.receiver.util.evaluators.ExpressionEvaluator;
import com.hercules.android.receiver.util.evaluators.InvalidAndroidReceiverStateException;
import com.hercules.android.receiver.util.evaluators.InvalidExpressionException;
import com.hercules.android.receiver.util.evaluators.InvalidPathException;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

/**
 * The handler used to process XRESetCommand. It will update the specified XREObject and paint affected views. It
 * extends XREObjectCommandHandler for convenience.
 * <p>
 * <b>Sample Usage:</b> Please look at sample usage of {@link XREAggregateCommandHandler}.
 * <p>
 * <b>Thread Safety:</b> This class is not thread safe since it has mutable fields. But if they are not changed after
 * first configured, then this class is used in a thread safe manner.
 *
 * @author liuliquan, 7realm
 * @author 7realm
 * @version 1.0
 */
public class XRESetCommandHandler extends XREObjectCommandHandler {
    /** Represents size of matrix. */
    private static final int MATRIX_SIZE = 4;

    /**
     * The evaluator used to evaluate the expression values.
     * <p>
     * Initial value is null. Not null. It has getter and setter.
     */
    private ExpressionEvaluator expressionEvaluator;

    /**
     * Represents XREView event binder. It used to bind XREView events (OnKeyUp, OnMouseUp, ...) to UI views.
     * <p>
     * Can be any value, has getter and setter. Is used in processCommandMessage method.
     * <p>
     * AndroidAssembly1 new field, together with getter and setter
     */
    private EventListenerBinder binder;

    /**
     * Default empty constructor.
     */
    public XRESetCommandHandler() {
        // empty
    }

    /**
     * Gets the XREView event binder.
     *
     * @return the xREView event binder
     */
    public EventListenerBinder getBinder() {
        return binder;
    }

    /**
     * Sets the XREView event binder.
     *
     * @param binder the xREView event binder
     */
    public void setBinder(EventListenerBinder binder) {
        this.binder = binder;
    }

    /**
     * Handle given SET command message. The target resource or view will be updated. The affected views will be
     * repainted.
     *
     * @param command the XRE SET command to be handled
     * @param receiver the receiver for the XRE application
     * @throws IllegalStateException if expression evaluator is not set
     * @throws IllegalArgumentException if given command or receiver is null
     * @throws DuplicateXRECommandParameterException if there is a parameter exists in both XRESetCommand#props and
     * XRESetCommand#runtimeExpressionValues map
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     * @throws UnsupportedXRECommandParameterException if any command parameter is not supported
     * @throws XREObjectNotFoundException if the XREObject to be updated or any XREObject to be associated is not found
     * @throws XREViewFinderUtilityException if any error occurs while finding the affected views
     * @throws XREObjectCommandHandlerException if any other error occurs while handling the command (including
     * evaluating the expression values)
     */
    @SuppressWarnings("boxing")
    @Override
    protected void processCommandMessage(XRECommand command, AndroidReceiver receiver)
        throws XREObjectCommandHandlerException {
        String methodName = "processCommandMessage";
        LogUtil.enter(getLogger(), this, methodName, command, receiver);

        try {
            // check state
            CommandHandlersUtil.checkState(expressionEvaluator, "expressionEvaluator");
            CommandHandlersUtil.checkState(getBinder(), "binder");

            // check arguments
            XRESetCommand setCommand = CommandHandlersUtil.checkCommand(command, XRESetCommand.class);
            CommandHandlersUtil.checkReceiver(receiver);

            // call pre painting handler
            callPrePaintHandler(receiver, setCommand);

            // get object by given id
            XREObject xreObject =
                receiver.getApplicationData().getElementsXREIdToObject().get(setCommand.getTargetId());
            if (xreObject == null) {
                throw new XREObjectNotFoundException("Object with given ID '" + setCommand.getTargetId()
                    + "' does not exist.", setCommand.getTargetId());
            }

            // shallow copy command properties
            Map<String, Object> params =
                setCommand.getProps() == null ? new TreeMap<String, Object>() : new TreeMap<String, Object>(setCommand
                    .getProps());

            // setup event handlers
            CommandHandlersUtil.setupHandlers(getLogger(), this, methodName, xreObject, params, receiver
                .getApplicationData().getElementsXREIdToObject(), receiver, null);

            // use affectedViewFinder utility to find affected views
            List<XREView> affectedViews = getAffectedViewFinder().getAffectedViews(setCommand, receiver);

            populate(receiver, setCommand, xreObject, params);

            // check if all properties were processed
            CommandHandlersUtil.checkParams(params, "props", getLogger(), this, methodName);
            
            // paint affect views
            paintAffectedViews(affectedViews, setCommand, receiver);
            LogUtil.exit(getLogger(), this, methodName);
        } catch (IllegalArgumentException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        } catch (IllegalStateException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        } catch (XREObjectCommandHandlerException e) {
            throw LogUtil.error(getLogger(), this, methodName, e);
        } catch (ValidationException e) {
            throw LogUtil.error(getLogger(), this, methodName, new XREObjectCommandHandlerException(e.getMessage(), e));
        }
    }

    /**
     * Populate object using data from command.
     *
     * @param receiver current {@link AndroidReceiver}
     * @param setCommand the set command
     * @param xreObject the target object
     * @param params the parameters of command
     * @throws XREObjectCommandHandlerException if any error occurs
     */
    @SuppressWarnings("boxing")
    private void populate(AndroidReceiver receiver, XRESetCommand setCommand, XREObject xreObject,
        Map<String, Object> params) throws XREObjectCommandHandlerException {
        if (xreObject instanceof XREView) {
            populateView((XREView) xreObject, params, receiver);
        } else if (xreObject instanceof XREText) {
            populateText((XREText) xreObject, params, receiver);
        } else if (xreObject instanceof XRETextInput) {
            populateTextInput((XRETextInput) xreObject, params);
        } else if (xreObject instanceof XREStylesheet) {
            populateStylesheet((XREStylesheet) xreObject, params);
        } else if (xreObject instanceof XRERectangle) {
            populateRectangle((XRERectangle) xreObject, params, receiver);
        } else if (xreObject instanceof XREImage) {
            populateImage((XREImage) xreObject, params, receiver);
        } else if (xreObject instanceof XRESound || xreObject instanceof XREVideo || xreObject instanceof DoNothingSound) {
            populateStreamResource((XREStreamResource) xreObject, params, receiver);
        } else if (xreObject instanceof XREApplication) {
            populateApplication((XREApplication) xreObject, params);
        } else if (xreObject instanceof XREHTMLText) {
            populateHTMLText((XREHTMLText) xreObject, params);
        } else if (xreObject instanceof XREFont || xreObject instanceof XREFlash
            || xreObject instanceof XREAnimationResource || xreObject instanceof XREHTMLView
            || xreObject instanceof XREServiceProxy) {
            if (params.size() > 0) {
                // AndroidAssembly3 corrected bug, added logging
                // log all unsupported properties
                LogUtil.warn(getLogger(), this, "processCommandMessage", "SET command for target object class {0} "
                    + "should not have any properties or runtime expression.", xreObject.getClass().getSimpleName());
                for (Entry<String, Object> entry : params.entrySet()) {
                    LogUtil.warn(getLogger(), this, "processCommandMessage", "Unsupported runtime property {0}.", entry
                        .getKey());
                }
            }
        } else {
            throw new InvalidXRECommandParameterException("Target object class '"
                + xreObject.getClass().getSimpleName() + "' is not supported.", "targetId", setCommand.getTargetId());
        }
    }

    /**
     * Gets the evaluator used to evaluate the expression values.
     *
     * @return the evaluator used to evaluate the expression values
     */
    public ExpressionEvaluator getExpressionEvaluator() {
        return expressionEvaluator;
    }

    /**
     * Sets the evaluator used to evaluate the expression values.
     *
     * @param expressionEvaluator the evaluator used to evaluate the expression values
     * @throws IllegalArgumentException if given evaluator is null
     */
    public void setExpressionEvaluator(ExpressionEvaluator expressionEvaluator) {
        // check argument
        CommandHandlersUtil.checkNotNull(expressionEvaluator, "expressionEvaluator");

        this.expressionEvaluator = expressionEvaluator;
    }

    /**
     * Populates {@link XREView} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @param receiver the receiver for the XRE application
     * @throws XREObjectCommandHandlerException if XREView's parent do not have set children
     */
    @SuppressWarnings("boxing")
    private void populateView(XREView xreObject, Map<String, Object> params, AndroidReceiver receiver)
        throws XREObjectCommandHandlerException {
        // evaluate expressions
        evaluateDouble(xreObject.getId(), receiver, params, "alpha");
        evaluateInteger(xreObject.getId(), receiver, params, "width");
        evaluateInteger(xreObject.getId(), receiver, params, "height");
        evaluateInteger(xreObject.getId(), receiver, params, "resource");
        evaluateInteger(xreObject.getId(), receiver, params, "mask");
        evaluateDimensions(xreObject.getId(), receiver, params, "dimensions");
        evaluateMatrix(xreObject.getId(), receiver, params, "matrix");
        evaluateTextPositions(xreObject.getId(), receiver, params);

        Map<Integer, XREObject> elementsMap = receiver.getApplicationData().getElementsXREIdToObject();

        // set properties of XREView
        xreObject.setAlpha(CommandHandlersUtil.getNum(params, "alpha", false, true, 1d, xreObject.getAlpha()));
        int[] dimensions = CommandHandlersUtil.getDimensions(params, xreObject.getDimensions());
        dimensions[0] = CommandHandlersUtil.getNum(params, "width", false, true, -1, dimensions[0]);
        dimensions[1] = CommandHandlersUtil.getNum(params, "height", false, true, -1, dimensions[1]);
        xreObject.setDimensions(dimensions);
        xreObject.setMatrix(CommandHandlersUtil.getMatrix(params, xreObject.getMatrix()));
        xreObject.setResource(CommandHandlersUtil.getById(params, "resource", elementsMap, XREResource.class, xreObject
            .getResource()));
        xreObject.setResourceOptions(CommandHandlersUtil.getValue(params, "resourceOptions", XREResourceOptions.class,
            xreObject.getResourceOptions()));
        xreObject
            .setMask(CommandHandlersUtil.getById(params, "mask", elementsMap, XREImage.class, xreObject.getMask()));
        xreObject.setClip(CommandHandlersUtil.getBool(params, "clip", false, xreObject.isClip()));
        xreObject.setIgnoreMouse(CommandHandlersUtil.getBool(params, "ignoreMouse", false, xreObject.isIgnoreMouse()));
        xreObject.setVisible(CommandHandlersUtil.getBool(params, "visible", false, xreObject.isVisible()));
        xreObject.setPainting(CommandHandlersUtil.getBool(params, "painting", false, xreObject.isPainting()));
    }

    /**
     * Populates {@link XREText} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @param receiver the receiver for the XRE application
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private void populateText(XREText xreObject, Map<String, Object> params, AndroidReceiver receiver)
        throws XREObjectCommandHandlerException {
        // evaluate expression
        evaluateInteger(xreObject.getId(), receiver, params, "size");

        // populate properties
        xreObject.setText(CommandHandlersUtil.getString(params, "text", false, true, xreObject.getText()));
        xreObject.setSize(CommandHandlersUtil.getNum(params, "size", false, true, -1, xreObject.getSize()));
        xreObject.setColor(CommandHandlersUtil.getNum(params, "color", false, false, -1, xreObject.getColor()));
        xreObject.setBlinkingCursor(CommandHandlersUtil.getBool(params, "blinkingCursor", false, xreObject
            .isBlinkingCursor()));
        xreObject.setDocumentMargin(CommandHandlersUtil.getNum(params, "documentMargin", false, true, -1.0, xreObject
            .getDocumentMargin()));
    }

    /**
     * Populates {@link XRETextInput} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     */
    private static void populateTextInput(XRETextInput xreObject, Map<String, Object> params)
        throws InvalidXRECommandParameterException {
        xreObject.setText(CommandHandlersUtil.getString(params, "text", false, true, xreObject.getText()));
    }

    /**
     * Populates {@link XREStylesheet} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     */
    private static void populateStylesheet(XREStylesheet xreObject, Map<String, Object> params)
        throws InvalidXRECommandParameterException {
        xreObject.setCss(CommandHandlersUtil.getString(params, "css", false, false, xreObject.getCss()));
    }

    /**
     * Populates {@link XRERectangle} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @param receiver the receiver for the XRE application
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private void populateRectangle(XRERectangle xreObject, Map<String, Object> params, AndroidReceiver receiver)
        throws XREObjectCommandHandlerException {
        // evaluate expression
        evaluateDouble(xreObject.getId(), receiver, params, "borderThickness");

        xreObject.setColor(CommandHandlersUtil.getNum(params, "color", false, false, -1, xreObject.getColor()));
        xreObject.setBorderColor(CommandHandlersUtil.getNum(params, "borderColor", false, false, -1, xreObject
            .getBorderColor()));
        xreObject.setBorderThickness(CommandHandlersUtil.getNum(params, "borderThickness", false, true, -1d, xreObject
            .getBorderThickness()));
    }

    /**
     * Populates {@link XREImage} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     * @throws XREObjectPainterException 
     */
    private static void populateImage(XREImage xreObject, Map<String, Object> params, AndroidReceiver receiver)
        throws InvalidXRECommandParameterException {
        xreObject.setUrl(CommandHandlersUtil.getString(params, "url", false, false, xreObject.getUrl()));
        
        if (xreObject.getUrl() != null) {
        	try {
				PaintersUtil.loadImage(xreObject.getUrl(), receiver, xreObject);
			} catch (XREObjectPainterException e) {
				throw new InvalidXRECommandParameterException("image load failed", e, "url", xreObject.getUrl());
			}
        }
    }

    /**
     * Populates {@link XREApplication} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     */
    @SuppressWarnings("unchecked")
    private static void populateApplication(XREApplication xreObject, Map<String, Object> params)
        throws InvalidXRECommandParameterException {
        xreObject.setLaunchParams(CommandHandlersUtil.getValue(params, "launchParams", Map.class, xreObject
            .getLaunchParams()));
    }

    /**
     * Populates {@link XREHTMLText} object.
     * <p>
     * AndroidAssembly3 this method was added in Android Assembly 3.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @throws InvalidXRECommandParameterException if any command parameter has invalid value
     */
    private static void populateHTMLText(XREHTMLText xreObject, Map<String, Object> params)
        throws InvalidXRECommandParameterException {
        xreObject.setHtmlText(CommandHandlersUtil.getString(params, "htmlText", false, true, xreObject.getHtmlText()));
    }

    /**
     * Populates {@link XREVideo} object.
     *
     * @param xreObject the object to populate
     * @param params the command parameters
     * @param receiver the receiver for the XRE application
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private void populateStreamResource(XREStreamResource xreObject, Map<String, Object> params,
        AndroidReceiver receiver) throws XREObjectCommandHandlerException {
        // evaluate expressions
        evaluateInteger(xreObject.getId(), receiver, params, "volume");
        evaluateInteger(xreObject.getId(), receiver, params, "position");
        evaluateInteger(xreObject.getId(), receiver, params, "speed");

        // populate properties
        xreObject.setVolume(CommandHandlersUtil.getNum(params, "volume", false, true, CommandHandlersUtil.MAX_VOLUME,
            xreObject.getVolume()));
        xreObject.setPosition(CommandHandlersUtil.getNum(params, "position", false, true, -1, xreObject.getPosition()));
        xreObject.setSpeed(CommandHandlersUtil.getNum(params, "speed", false, false, -1d, xreObject.getSpeed()));
    }

    /**
     * Evaluates integer expression.
     * <p>
     * AndroidAssembly2 new method
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param params the object parameters
     * @param name the name of property to evaluate
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private void evaluateInteger(int id, AndroidReceiver receiver, Map<String, Object> params, String name)
        throws XREObjectCommandHandlerException {
        // this will check also not null value
        if (params.get(name) instanceof String) {
            params.put(name, evaluateExpression(id, receiver, (String) params.get(name)).intValue());
        }
    }

    /**
     * Evaluates double expression.
     * <p>
     * AndroidAssembly2 new method
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param params the object parameters
     * @param name the name of property to evaluate
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    private void evaluateDouble(int id, AndroidReceiver receiver, Map<String, Object> params, String name)
        throws XREObjectCommandHandlerException {
        // this will check also not null value
        if (params.get(name) instanceof String) {
            params.put(name, evaluateExpression(id, receiver, (String) params.get(name)));
        }
    }

    /**
     * Evaluates dimensions expression.
     * <p>
     * AndroidAssembly2 new method
     * <p>
     * AndroidAssembly3 runtime properties wrapper changed to String array
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param params the object parameters
     * @param name the name of property to evaluate
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    private void evaluateDimensions(int id, AndroidReceiver receiver, Map<String, Object> params, String name)
        throws XREObjectCommandHandlerException {
        // this will check also not null value
        if (params.get(name) instanceof String[]) {
            // create array of integers
            String[] source = (String[]) params.get(name);
            int[] array = new int[source.length];

            // fill array with evaluated expressions
            for (int i = 0; i < array.length; i++) {
                array[i] = evaluateExpression(id, receiver, source[i]).intValue();
            }

            // put evaluated array
            params.put(name, array);
        }
    }

    /**
     * Evaluates matrix expression.
     * <p>
     * AndroidAssembly2 new method
     * <p>
     * AndroidAssembly3 this method was added in Android Assembly 3
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param params the object parameters
     * @param name the name of property to evaluate
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private void evaluateMatrix(int id, AndroidReceiver receiver, Map<String, Object> params, String name)
        throws XREObjectCommandHandlerException {
        // this will check also not null value
        if (params.get(name) instanceof String[]) {
            // create matrix of double
            String[] source = (String[]) params.get(name);
            double[][] matrix = new double[MATRIX_SIZE][MATRIX_SIZE];

            for (int i = 0; i < MATRIX_SIZE; i++) {
                for (int j = 0; j < MATRIX_SIZE; j++) {
                    matrix[j][i] = evaluateExpression(id, receiver, source[i * 4 + j]);
                }
            }

            // put evaluated matrix
            params.put(name, matrix);
        }
    }

    /**
     * Evaluates text positions at expressions in {@link XREResourceOptions}.
     * <p>
     * AndroidAssembly3 this method was added in Android Assembly 3.
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param params the object parameters
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    private void evaluateTextPositions(int id, AndroidReceiver receiver, Map<String, Object> params)
        throws XREObjectCommandHandlerException {
        // this will check also not null value
        if (params.get("resourceOptions") instanceof XREResourceOptions) {
            XREResourceOptions options = (XREResourceOptions) params.get("resourceOptions");

            // process start positions
            if (options.getTextStartPos() instanceof String[]) {
                double[] array = convertArray(id, receiver, (String[]) options.getTextStartPos());
                options.setTextStartPos(array);
            }

            // process stop positions
            if (options.getTextStopPos() instanceof String[]) {
                double[] array = convertArray(id, receiver, (String[]) options.getTextStopPos());
                options.setTextStopPos(array);
            }
        }
    }

    /**
     * Evaluate array of strings to array of doubles.
     * <p>
     * AndroidAssembly3 new method
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param source the array of expressions
     * @return the evaluated array
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    private double[] convertArray(int id, AndroidReceiver receiver, String[] source)
        throws XREObjectCommandHandlerException {
        double[] array = new double[2];

        // fill array with evaluated expressions
        for (int i = 0; i < 2; i++) {
            array[i] = evaluateExpression(id, receiver, source[i]).doubleValue();
        }
        return array;
    }

    /**
     * Evaluates dimensions expression.
     * <p>
     * AndroidAssembly2 new method
     *
     * @param id the id of current object
     * @param receiver the receiver for the XRE application
     * @param expression the expression to evaluate
     * @return the evaluated value
     * @throws XREObjectCommandHandlerException if error occurred while evaluating expression
     */
    @SuppressWarnings("boxing")
    private Double evaluateExpression(int id, AndroidReceiver receiver, String expression)
        throws XREObjectCommandHandlerException {
        try {
            // evaluate
            Double value = expressionEvaluator.evaluateExpression(expression, id, receiver);

            // return value as int or as double
            return value;
        } catch (IllegalArgumentException e) {
            throw new XREObjectCommandHandlerException("Incorrect argument passed to expression evaluator.", e);
        } catch (InvalidExpressionException e) {
            throw new XREObjectCommandHandlerException("Passed expression has invalid format.", e);
        } catch (InvalidAndroidReceiverStateException e) {
            throw new XREObjectCommandHandlerException("Android receiver is not in correct state.", e);
        } catch (InvalidPathException e) {
            throw new XREObjectCommandHandlerException("Object's path specified in the expression is invalid.", e);
        }
    }
}